#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>

#include "ff.h"


BYTE *RamDisk;		/* Poiter to the RAM disk */
DWORD RamDiskSize;	/* Size of RAM disk in unit of sector */

static FATFS FatFs;
static FIL DstFile;
static char SrcPath[512], DstPath[512];
static BYTE Buff[4096];
static UINT Dirs, Files;

// 递归遍历目录函数
int maketree (void)
{
    DIR *hdir;
    FILE *fsrc;
    struct dirent *entry;
    struct stat statbuf;
    int slen, dlen, rv = 0;
    DWORD br;
    UINT bw;
    FRESULT res;

    slen = strlen(SrcPath);
    dlen = strlen(DstPath);
        
    hdir = opendir(SrcPath); /* Open directory */
    if (hdir == NULL)
    {
        printf("Failed to open the source directory (%s).\n", SrcPath);
    }
    else
    {
        // 遍历目录中的每个条目
        while ((entry = readdir(hdir)) != NULL)
        {
            rv = 1;

            // 跳过 "." 和 ".." 目录
            if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
            {
                continue;
            }

            // 构建完整路径
            sprintf(&SrcPath[slen], "/%s", entry->d_name);
            sprintf(&DstPath[dlen], "/%s", entry->d_name);

            // 获取文件/目录信息
            if (lstat(SrcPath, &statbuf) == -1) {
                perror("lstat");
                continue;
            }

            if (S_ISDIR(statbuf.st_mode))
            {
                /* The item is a directory */
                printf("src dir: %s\n", SrcPath);
                if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, ".."))
                {
                    res = f_mkdir(DstPath);
                    if (res != FR_OK)
                    { /* Create destination directory */
                        printf("Failed to create directory '%s' (%d).\n", DstPath, res);
                        break;
                    }
                    if (!maketree())
                        break; /* Enter the directory */
                    Dirs++;
                }
            }
            else
            {
                /* The item is a file */
                printf("src file: %s\n", SrcPath);
                fsrc = fopen(SrcPath, "rb+");
                if (fsrc == NULL)
                {
                    /* Open source file */
                    printf("Failed to open source file.\n");
                    break;
                }
                if (f_open(&DstFile, DstPath, FA_CREATE_ALWAYS | FA_WRITE))
                {
                    /* Create destination file */
                    printf("Failed to create destination file.\n");
                    break;
                }

                do
                {
                    /* Copy source file to destination file */
                    br = fread(Buff, 1, sizeof(Buff), fsrc);

                    if (br == 0) break;

                    f_write(&DstFile, Buff, (UINT)br, &bw);
                } while (br == bw);
                fclose(fsrc);
                f_close(&DstFile);
                if (br && br != bw)
                {
                    printf("Failed to write file (%s) br=%d,bw=%d.\n", DstPath, br, bw);
                    break;
                }
                Files++;
            }
        }
        closedir(hdir);
    }
    SrcPath[slen] = 0;
	DstPath[dlen] = 0;
	return rv;
}

int main(int argc, char **argv)
{
    UINT csz;
    FILE *wh;
    DWORD wb, szvol;
    int ai = 1;
    const char *outfile;

    if (argc < 3) {
        printf("usage: mkfatimg [-t] <source node> <output image> <image size> [<cluster size>]\n"
                "    <source node>: Source node as root of output image\n"
                "    <output image>: FAT volume image file\n"
                "    <image size>: Size of output image in unit of KiB\n"
                "    <cluster size>: Size of cluster in unit of byte (default:512)\n"
            );
        return 1;
    }
    strcpy(SrcPath, argv[ai++]);
    outfile = argv[ai++];
    RamDiskSize = atoi(argv[ai++]) * 1024;
    csz = (argc >= 5) ? atoi(argv[ai++]) : 512;

    /* Create an FAT volume (Supports only FAT/FAT32) */
    if (f_mkfs("", NULL, Buff, sizeof(Buff)))
    {
        printf("Failed to create FAT volume. Adjust volume size or cluster size.\n");
        return 2;
    }

    /* Copy source directory tree into the FAT volume */
    f_mount(&FatFs, "", 0);
    if (!maketree()) return 3;
    if (!Files) { printf("No file in the source directory."); return 3; }

    /* Output the FAT volume to the file */
    printf("\nWriting output file...");
    wh = fopen(outfile, "wb+");
    printf("create output file (%s).\n", outfile);
    if (wh == NULL)
    {
        printf("Failed to create output file (%s).\n", outfile);
        return 4;
    }

    szvol = RamDiskSize / 1024;
    printf("RamDiskSize = %dKB\n", szvol);
    fwrite(RamDisk, sizeof(uint8_t), RamDiskSize, wh);
    fclose(wh);

    printf("\n%u files and %u directories in the %uKiB of FAT volume.\n", Files, Dirs, RamDiskSize / 1024);    

    return 0;
}

